use anyhow::{anyhow, Result};
use regex::{Captures, Regex};
use std::fs;
use std::fs::OpenOptions;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::path::{Path, PathBuf};

use crate::service::{run_cli, AppData, ParamType};
#[cfg(target_family = "unix")]
use crate::ssh::{SshUtil};
use crate::utils::FILE_DELIMITER;
use crate::{log, service, success, WORK_DIR};

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ServerItem {
    pub label: String,
    pub desc: String,
    pub ip: String,
    pub port: i32,
    pub user_name: String,
    pub auth_type: i32,
    pub password: String,
    pub secret_key: String,
    pub deploy_path: String,
}

pub fn add_server(app: &mut AppData) -> Result<()> {
    log("--------- 新增服务器 ---------");
    let label = run_cli("名称".to_string(), ParamType::String, None, None, false)?.as_string().expect("参数错误！");
    let desc = run_cli("描述".to_string(), ParamType::String, None, None, false)?.as_string().expect("参数错误！");
    let ip = run_cli("IP".to_string(), ParamType::String, None, None, false)?.as_string().expect("参数错误！");
    let port = run_cli("端口".to_string(), ParamType::Inetger, None, None, false)?.as_int().expect("参数错误！");
    let port = port as i32;
    let user_name = run_cli("用户名".to_string(), ParamType::String, None, None, false)?.as_string().expect("参数错误！");
    let auth_type = run_cli("验证方式".to_string(), ParamType::Select, Some(vec!["密码".to_string(), "秘钥".to_string()]), Some("0".to_string()), false)?.as_int().expect("参数错误！");

    let mut password: String = "".to_string();
    let mut secret_key: String = "".to_string();
    if auth_type == 0 {
        password = run_cli("密码".to_string(), ParamType::Password, None, None, false)?.as_string().expect("参数错误！");
    } else {
        secret_key = run_cli("秘钥路径".to_string(), ParamType::String, None, None, false)?.as_string().expect("参数错误！");
    }
    let server = ServerItem { label, desc, ip, port, user_name, auth_type: auth_type as i32, password, secret_key, deploy_path: "".to_string() };
    app.add_server(server)?;
    Ok(())
}

pub fn update_server(app: &mut AppData, label: String) -> Result<()> {
    log("--------- 修改服务器(默认不修改) ---------");
    let server = app.get_server(label.clone())?;

    let desc: String = run_cli("名称".to_string(), ParamType::String, None, Some(server.desc.clone()), true)?.as_string().expect("参数错误！");
    let desc = if desc.is_empty() { server.desc.clone() } else { desc };

    let ip: String = run_cli("IP".to_string(), ParamType::String, None, Some(server.ip.clone()), true)?.as_string().expect("参数错误！");
    let ip = if ip.is_empty() { server.ip.clone() } else { ip };

    let port = run_cli("端口".to_string(), ParamType::Inetger, None, Some(format!("{}", server.port)), true)?.as_int();
    let port = if port.is_none() { server.port } else { port.expect("参数错误！") as i32 };

    let user_name: String = run_cli("用户名".to_string(), ParamType::String, None, Some(server.user_name.clone()), true)?.as_string().expect("参数错误！");
    let user_name = if user_name.is_empty() { server.user_name.clone() } else { user_name };

    let auth_type = run_cli("验证方式".to_string(), ParamType::Select, Some(vec!["密码".to_string(), "秘钥".to_string()]), Some(server.auth_type.to_string()), true)?.as_int().expect("参数错误！");

    let mut password: String = "".to_string();
    let mut secret_key: String = "".to_string();
    if auth_type == 0 {
        password = run_cli("密码".to_string(), ParamType::Password, None, None, true)?.as_string().expect("参数错误！");
        password = if password.is_empty() { server.password.clone() } else { password };
    } else {
        secret_key = run_cli("秘钥路径".to_string(), ParamType::String, None, Some(server.secret_key.clone()), true)?.as_string().expect("参数错误！");
        secret_key = if secret_key.is_empty() { server.secret_key.clone() } else { secret_key };
    }
    let confirmation = run_cli("确认修改?".to_string(), ParamType::Confirm, None, None, false)?.as_bool().expect("参数错误！");
    if confirmation {
        let new_server = ServerItem {
            label,
            desc,
            ip,
            port,
            user_name,
            auth_type: auth_type as i32,
            password,
            secret_key,
            deploy_path: "".to_string(),
        };
        app.update_server(new_server)?;
    }
    Ok(())
}

pub fn remove_server(app: &mut AppData, label: String) -> Result<()> {
    let confirmation = run_cli(format!("确认删除[{}]?", label), ParamType::Confirm, None, None, false)?.as_bool().expect("参数错误！");
    if confirmation {
        app.del_server(label)?;
    }
    Ok(())
}

pub fn select_server(app: &AppData, select: bool) -> Result<String> {
    if app.servers.is_empty() {
        return Err(anyhow!("无服务器信息！"));
    }
    let label: Vec<String> = app.servers.iter().map(|it| format!("{}({}) - {}", it.label, it.desc, it.ip)).collect();
    if select {
        let select = run_cli("请选择服务器(默认选择第一个)".to_string(), ParamType::Select, Some(label), Some("0".to_string()), false)?.as_int().expect("参数错误！");
        let key = app.servers[select].label.clone();
        Ok(key)
    } else {
        for x in label {
            log(x);
        }
        Ok("".to_string())
    }
}

pub fn show_server(label: &str) -> Result<()> {
    let app = AppData::read_data()?;
    let server = app.get_server(label.to_string())?;
    success(format!("名  称：{}\n描  述：{}\n主  机：{}\n端  口：{}\n用户名：{}\n密  码：{}\n密  钥：{}\n",
                    server.label, server.desc, server.ip, server.port, server.user_name, server.password, server.secret_key));
    Ok(())
}

fn get_loacl_file(ssh: &mut SshUtil, local_file: PathBuf, remote: PathBuf) -> Result<Vec<(PathBuf, PathBuf)>> {
    let mut local_files = vec![];
    if !local_file.exists() {
        return Err(anyhow!("{:?} 不存在！", local_file));
    }
    if local_file.is_file() {
        let file_name = local_file.file_name().unwrap_or_else(|| panic!("获取文件名错误！")).to_string_lossy().to_string();
        local_files.push((local_file, remote.join(file_name)));
    } else {
        let files = local_file.read_dir()?;
        let file_name = local_file.file_name().unwrap_or_else(|| panic!("获取文件名错误！"));
        let current = remote.join(file_name);
        ssh.check_remote_dir(&current)?;
        for file in files {
            let fs = file?;
            local_files.extend(get_loacl_file(ssh, fs.path(), current.clone())?);
        }
    }
    Ok(local_files)
}

fn get_remote_file(local: &Path, remote_files: Vec<PathBuf>, remote_file: &Path) -> Result<Vec<(PathBuf, PathBuf)>> {
    let mut files = Vec::new();

    let local_str = local.to_string_lossy().to_string();
    let mut remote_temp = remote_file.to_path_buf();
    remote_temp.pop();
    let remote_str = format!("{}{}", remote_temp.to_string_lossy(), FILE_DELIMITER);
    for remote in remote_files {
        let remote_path = remote.to_string_lossy().to_string();
        let local_path = PathBuf::from(remote_path.replace(&remote_str, &local_str));
        let mut temp = local_path.clone();
        temp.pop();
        if !temp.exists() {
            std::fs::create_dir_all(temp)?;
        }
        files.push((local_path, remote));
    }
    Ok(files)
}

fn check_file_path(local: &String, remote: &String, current: &String, user: &String) -> (PathBuf, PathBuf) {
    let local_file = if local.starts_with(FILE_DELIMITER) {
        PathBuf::from(local)
    } else if local.starts_with(&format!("~{}", FILE_DELIMITER)) {
        let home = dirs::home_dir().unwrap_or_else(|| panic!("获取用户目录失败！"));
        Path::new(&home).join(local.replace(&format!("~{}", FILE_DELIMITER), ""))
    } else {
        Path::new(current).join(local.replace(&format!(".{}", FILE_DELIMITER), ""))
    };
    let remote_file = if remote.starts_with("~/") || remote.starts_with("./") {
        if user.eq("root") {
            PathBuf::from("/root").join(remote.replace("./", "").replace("~/", ""))
        } else {
            PathBuf::from(format!("/home/{}", user)).join(remote.replace("./", "").replace("~/", ""))
        }
    } else {
        PathBuf::from(remote)
    };
    (local_file, remote_file)
}

pub async fn scp_file(args: Vec<String>) -> Result<()> {
    let first = args[0].clone();
    let last = args[args.len() - 1].clone();
    let len = args.len();

    if !first.contains(':') && !last.contains(':') {
        return Err(anyhow!("参数错误！(上传： file ... [server]:[path]，下载：[server]:[path] file ...)"));
    }

    let mut oper_type = 0;
    let (server_name, oper_path) = if first.contains(':') {
        let tmp: Vec<&str> = first.split(':').collect();
        (tmp[0], tmp[1])
    } else {
        oper_type = 1;
        let tmp: Vec<&str> = last.split(':').collect();
        (tmp[0], tmp[1])
    };
    let app = AppData::read_data()?;
    let server = app.get_server(server_name.to_string())?;
    let mut ssh = SshUtil::login_server(server)?;
    let current = WORK_DIR.get().unwrap_or_else(|| panic!("获取工作目录失败！"));
    if oper_type == 0 {
        let mut remote_path = Vec::from(&args[1..len - 1]);
        remote_path.insert(0, oper_path.to_string());

        let mut remote_files = Vec::new();
        for remote in remote_path {
            let (local_file, remote_file) = check_file_path(&last, &remote.to_string(), current, &server.user_name);
            let files = ssh.get_remote_dir(&remote_file)?;
            remote_files.extend(get_remote_file(&local_file, files, &remote_file)?);
        }
        ssh.multiple_scp(remote_files, false).await?;
    } else {
        let args = &args[0..(len - 1)];
        let mut local_files: Vec<(PathBuf, PathBuf)> = Vec::new();
        for file in args {
            let (local_file, remote_file) = check_file_path(file, &oper_path.to_string(), current, &server.user_name);
            local_files.extend(get_loacl_file(&mut ssh, local_file, remote_file)?);
        }
        ssh.multiple_scp(local_files, true).await?;
    }
    Ok(())
}

#[cfg(target_family = "unix")]
pub async fn login_ssh(server: String) -> Result<()> {
    let mut plugin_cls = service::Plugin::new()?;
    plugin_cls.run(&"rssh".to_string(), vec![server])?;
    Ok(())
}

fn search_file(file_path: PathBuf, file_name: &String) -> Result<Vec<PathBuf>> {
    let mut target = Vec::new();
    let dir = file_path.read_dir()?;
    for item in dir {
        let file = item?;
        if file.path().is_dir() {
            target.extend(search_file(file.path(), file_name)?);
        } else if file.file_name().to_string_lossy().to_string().eq(file_name) {
            target.push(file.path());
        }
    }
    Ok(target)
}

pub fn edit_file(file: &String, actions: Vec<String>, dir: Option<&String>) -> Result<()> {
    let work_dir = WORK_DIR.get().expect("获取工作目录失败！");
    let mut target_file = Vec::new();
    if dir.is_none() {
        target_file.push(Path::new(work_dir).join(file).canonicalize()?);
    } else {
        let path = if dir.unwrap().starts_with(FILE_DELIMITER) {
            PathBuf::from(dir.unwrap())
        } else {
            Path::new(work_dir).join(dir.unwrap()).canonicalize()?
        };
        target_file.extend(search_file(path, file)?);
    }

    for target in target_file {
        edit_single_file(&actions, target, work_dir)?;
    }
    Ok(())
}

fn edit_single_file(actions: &Vec<String>, file_path: PathBuf, work_dir: &String) -> Result<()> {
    log(format!("修改文件：{:?}", file_path));
    let file_name = file_path.file_name().expect("获取文件名错误！").to_string_lossy().to_string();
    let temp_file_path = Path::new(work_dir).join(format!(".{}.swap", file_name));

    let temp_file = OpenOptions::new().create(true).truncate(true).write(true).open(temp_file_path.clone())?;

    let mut writer = BufWriter::new(temp_file);

    let fs = OpenOptions::new().read(true).open(file_path.clone())?;

    let mut lines = BufReader::new(fs).lines();

    let has_positon_reg = Regex::new(r"(\d+.*)\s/(\S+..[\\\S+]*)/(\S+..[\\\S+]*)/")?;
    let no_positon_reg = Regex::new(r"/(\S+..[\\\S+]*)/(\S+..[\\\S+]*)/")?;

    let mut action_args = vec![];

    let get_capture_arg = |capture: &Captures, index: usize| -> String {
        return if let Some(value) = capture.get(index) {
            Regex::new(r"(\\)").expect("获取正则表达式错误！").replace_all(value.as_str(), "").to_string()
        } else {
            "".to_string()
        };
    };
    for action in actions {
        let captures = has_positon_reg.captures(action);
        if captures.is_none() {
            let captures = no_positon_reg.captures(action).expect("无法匹配参数！");
            action_args.push((vec![], get_capture_arg(&captures, 1), get_capture_arg(&captures, 2)));
        } else {
            let captures = captures.expect("无法匹配参数！");
            let positon: Vec<i32> = get_capture_arg(&captures, 1).split(',').map(|x| x.parse::<i32>().expect("参数错误！")).collect();
            if positon.len() > 1 && positon[0] >= positon[1] {
                panic!("参数错误！");
            }
            action_args.push((positon, get_capture_arg(&captures, 2), get_capture_arg(&captures, 3)));
        }
    }
    let mut current_line = 1;
    while let Some(Ok(line)) = lines.next() {
        let mut new_line = String::new();
        for (positon, old, new) in action_args.iter() {
            if (positon.is_empty() || positon.contains(&current_line)) && line.contains(old) {
                new_line = Regex::new(old)?.replace_all(&line, new).to_string();
            }
        }
        new_line = if new_line.is_empty() { line } else { new_line };
        writeln!(writer, "{}", new_line)?;
        current_line += 1;
    }
    writer.flush()?;
    fs::copy(temp_file_path.clone(), file_path)?;
    fs::remove_file(temp_file_path)?;
    Ok(())
}
